package com.nvwa.coding.model;

import com.nvwa.coding.util.safe.Safer;

import java.math.BigDecimal;
import java.util.Objects;
import java.util.function.Predicate;
import java.util.function.Supplier;


/**
 * 担保数据符合预期
 * @param <T> 数据类型
 */
public class Ensurer<T> {

    /**
     * 提供默认值
     */
    protected final Supplier<T> def;

    /**
     * 校验目标值，无论如何一定会前置校验nonNull
     */
    protected final Predicate<T> tester;

    protected Ensurer(Supplier<T> def, Predicate<T> tester) {
        this.def = def;
        this.tester = tester;
    }

    /**
     * 根据默认值构造
     *
     * @param def 默认值
     * @param <T> 类型
     */
    public static <T> Ensurer<T> of(T def) {
        return new Ensurer<>(() -> def, Objects::nonNull);
    }

    /**
     * 根据默认值函数构造
     *
     * @param def 提供默认值的函数
     * @param <T> 类型
     */
    public static <T> Ensurer<T> ofSupplier(Supplier<T> def) {
        return new Ensurer<>(def, Objects::nonNull);
    }

    /**
     * 根据默认值和校验器构造
     *
     * @param def    默认值
     * @param tester 校验器，会在校验器之前自动追加判空逻辑
     * @param <T>    类型
     */
    public static <T> Ensurer<T> of(T def, Predicate<T> tester) {
        return new Ensurer<>(() -> def, tester);
    }

    /**
     * 根据默认值函数和校验器构造
     *
     * @param def    默认值函数
     * @param tester 校验器，会在校验器之前自动追加判空逻辑
     * @param <T>    类型
     */
    public static <T> Ensurer<T> ofSupplier(Supplier<T> def, Predicate<T> tester) {
        return new Ensurer<>(def, tester);
    }

    /**
     * 修改默认值
     *
     * @param def 修改后的默认值
     * @return this
     */
    public Ensurer<T> def(T def) {
        return new Ensurer<>(() -> def, tester);
    }

    /**
     * 修改默认值函数
     *
     * @param def 修改后的默认值函数
     * @return this
     */
    public Ensurer<T> def(Supplier<T> def) {
        return new Ensurer<>(def, tester);
    }

    /**
     * 修改校验器
     *
     * @param tester 修改后的校验器，校验器会自动前置判空逻辑
     * @return this
     */
    public Ensurer<T> want(Predicate<T> tester) {
        return new Ensurer<>(def, o -> o != null && tester.test(o));
    }

    /**
     * 保证目标数据可用，否则用默认值替换之
     *
     * @param value 目标数据
     * @return 可用的目标数据或默认值
     */
    public T ensure(T value) {
        return check(value) ? value : def.get();
    }

    /**
     * 保证目标数据可用，依次校验提供的目标数据，选择第一个可用的数据，如果没有可用的数据，用默认值替换之
     *
     * @param values 目标数据集合
     * @return 可用的目标数据或默认值
     */
    @SafeVarargs
    public final T ensure(T... values) {
        if (values != null && values.length > 0) {
            for (T value : values) {
                if (check(value)) {
                    return value;
                }
            }
        }
        return def.get();
    }

    /**
     * 保证目标数据可用，否则用默认值替换之
     *
     * @param supplier 目标数据提供函数
     * @return 可用的目标数据或默认值
     */
    public T ensure(Supplier<T> supplier) {
        T value = supplier.get();
        return check(value) ? value : def.get();
    }

    /**
     * 保证目标数据可用，依次校验提供的目标数据，选择第一个可用的数据，如果没有可用的数据，用默认值替换之
     *
     * @param suppliers 目标数据提供函数的集合
     * @return 可用的目标数据或默认值
     */
    @SafeVarargs
    public final T ensure(Supplier<T>... suppliers) {
        if (suppliers != null && suppliers.length > 0) {
            for (Supplier<T> supplier : suppliers) {
                T value = supplier.get();
                if (check(value)) {
                    return value;
                }
            }
        }
        return def.get();
    }

    /**
     * 使用校验器校验目标数据，返回校验结果
     *
     * @param value 目标数据
     * @return 是否校验通过
     */
    public boolean check(T value) {
        return Safer.loggingGetOrElse(() -> tester.test(value), false);
    }

    /**
     * 使用校验器校验目标数据，返回校验结果
     *
     * @param supplier 目标数据的提供函数
     * @return 是否校验通过
     */
    public boolean check(Supplier<T> supplier) {
        return check(supplier.get());
    }









    public static final Ensurer<String> STR = Ensurer.of("");
    public static final Ensurer<Boolean> BOOL = Ensurer.of(false);
    public static final Ensurer<Byte> BYTE = Ensurer.of((byte) 0);
    public static final Ensurer<Short> SHORT = Ensurer.of((short) 0);
    public static final Ensurer<Integer> INT = Ensurer.of(0);
    public static final Ensurer<Long> LONG = Ensurer.of(0L);
    public static final Ensurer<Float> FLOAT = Ensurer.of(0F);
    public static final Ensurer<Double> DOUBLE = Ensurer.of(0D);
    public static final Ensurer<BigDecimal> BIG_DECIMAL = Ensurer.of(BigDecimal.ZERO);

    public static final Ensurer<Byte> POSITIVE_BYTE = Ensurer.of((byte) 1, i -> i > 0);
    public static final Ensurer<Short> POSITIVE_SHORT = Ensurer.of((short) 1, i -> i > 0);
    public static final Ensurer<Integer> POSITIVE_INT = Ensurer.of(1, i -> i > 0);
    public static final Ensurer<Long> POSITIVE_LONG = Ensurer.of(1L, i -> i > 0);
    public static final Ensurer<Float> POSITIVE_FLOAT = Ensurer.of(1F, i -> i > 0);
    public static final Ensurer<Double> POSITIVE_DOUBLE = Ensurer.of(1D, i -> i > 0);
    public static final Ensurer<BigDecimal> POSITIVE_BIG_DECIMAL = Ensurer.of(BigDecimal.ONE, i -> i.compareTo(BigDecimal.ZERO) > 0);

    public static final Ensurer<Byte> NATURAL_BYTE = Ensurer.of((byte) 0, i -> i >= 0);
    public static final Ensurer<Short> NATURAL_SHORT = Ensurer.of((short) 0, i -> i >= 0);
    public static final Ensurer<Integer> NATURAL_INT = Ensurer.of(0, i -> i >= 0);
    public static final Ensurer<Long> NATURAL_LONG = Ensurer.of(0L, i -> i >= 0);
    public static final Ensurer<Float> NATURAL_FLOAT = Ensurer.of(0F, i -> i >= 0);
    public static final Ensurer<Double> NATURAL_DOUBLE = Ensurer.of(0D, i -> i >= 0);
    public static final Ensurer<BigDecimal> NATURAL_BIG_DECIMAL = Ensurer.of(BigDecimal.ZERO, i -> i.compareTo(BigDecimal.ZERO) >= 0);

}
